package com.pi.shisan.service.audio;

import com.pi.shisan.service.util.ComponentManager;
import de.quippy.mp3.decoder.Bitstream;
import de.quippy.mp3.decoder.Decoder;
import de.quippy.mp3.decoder.Header;
import de.quippy.mp3.decoder.SampleBuffer;

import javax.sound.sampled.AudioFormat;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 此代码结构下不支持快进快退功能
 */
public class Mp3Player extends AudioPlayer {

    private Bitstream bitstream;
    private InputStream inputStream;

    private Decoder decoder;

    private byte[] firstData;

    public Mp3Player(File mp3File, AudioEffectComponentsSupplier<ComponentManager.MusicEffectBaseComponent> musicEffectComponentsSupplier) throws Exception {
        super(mp3File, musicEffectComponentsSupplier);
        init();
    }

    private void init() throws Exception {
        currentMilliseconds = 0;
        inputStream = new FileInputStream(audioFile);
        bitstream = new Bitstream(inputStream);
        Header header = bitstream.readFrame();
        if (header != null) {
            totalMilliseconds = (long) (header.total_ms((int) audioFile.length()) + 0.5);
        } else {
            throw new RuntimeException("不受支持的文件");
        }
        decoder = new Decoder();
        firstData = readDataAsFirst((SampleBuffer) decoder.decodeFrame(header, bitstream));
        bitstream.closeFrame();
    }

    @Override
    protected boolean doingPlay() throws Exception{
        if (audioOutputStream == null) {
            AudioFormat audioFormat = new AudioFormat(decoder.getOutputFrequency(), 16, decoder.getOutputChannels(), true, false);
            openSound(audioFormat);
        }
        if (firstData != null && firstData.length > 0) {
            currentMilliseconds += (((firstData.length >> 1) / decoder.getOutputChannels()) * 1000L) / decoder.getOutputFrequency();
            writeSampleData(firstData);
            firstData = null;
        }
        Header header;
        Bitstream bitstream = this.bitstream;
        Decoder decoder = this.decoder;
        AtomicInteger playerStatus = this.playerStatus;
        while (playerStatus.get() == PLAYING) {
            header = bitstream.readFrame();
            if (header == null) {
                return true;
            }
            SampleBuffer buffer = (SampleBuffer) decoder.decodeFrame(header, bitstream);
            bitstream.closeFrame();
            currentMilliseconds += ((buffer.getBufferLength() / decoder.getOutputChannels()) * 1000L) / decoder.getOutputFrequency();
            writeSampleData(readData(buffer));
        }
        return false;
    }

    @Override
    public void doDestroy() {
        closeSource();
    }

    /**
     * 播放完毕后循环再播一次
     */
    protected void reset() throws Exception{
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (Exception e) {}
        }
        if (bitstream != null) {
            try {
                bitstream.close();
            } catch (Exception e) {}
        }
        init();
    }

    private byte[] readDataAsFirst(SampleBuffer buffer) {
        short[] data = buffer.getBuffer();
        int len = buffer.getBufferLength();
        int dataLen = len << 1;//扩大二倍
        byte[] tmpData = getOutputBuffer(dataLen);
        int idx = 0;
        int pos = 0;
        short s;
        boolean hasData = false;
        while (len-- > 0) {
            s = data[pos++];
            tmpData[idx++] = (byte) (s & 0xFF);
            tmpData[idx++] = (byte) ((s >> 8) & 0xFF);
            if (!hasData && s != 0) {
                hasData = true;
            }
        }
        return hasData ? tmpData : EMPTY_DATA;
    }

    private byte[] readData(SampleBuffer buffer) {
        short[] data = buffer.getBuffer();
        int len = buffer.getBufferLength();
        int dataLen = len << 1;//扩大二倍
        byte[] tmpData = getOutputBuffer(dataLen);
        int idx = 0;
        int pos = 0;
        short s;
        while (len-- > 0) {
            s = data[pos++];
            tmpData[idx++] = (byte) (s & 0xFF);
            tmpData[idx++] = (byte) ((s >> 8) & 0xFF);
        }
        return tmpData;
    }

    private synchronized void closeSource() {
        if (bitstream != null) {
            try {
                bitstream.close();
            } catch (Exception e) {
            }
            bitstream = null;
        }
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (Exception e) {
            }
            inputStream = null;
        }
    }
}
